# -*- coding: UTF-8 -*-

from flask import Flask, request, redirect, abort, jsonify
import jwt, datetime, pymongo, time, sys, os, redis

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from exchange.utils.uuid import uuid
from exchange.com.db import db
from exchange.conf.conf import conf

app = Flask(__name__)



# jwt auth secret key
SECRET_KEY = "SECRET_KEY"



"""
用户鉴权
"""
def identify(request):
    try:
        auth_token = jwt.decode(request.headers.get('Authorization'), SECRET_KEY, algorithms='HS256')
        if auth_token:
            if not auth_token or auth_token['headers']['typ'] != 'JWT':
                return {"code": 400, "message": "jwt error!"}
            else:
                user = db.get_account(auth_token["headers"]["userid"])
                if not user:
                    return { "code": 400, "message": "用户不存在!" }
                else:
                    return { "code": 200, "message": "认证成功", "data": {"userid": user["userid"]} }
    except jwt.ExpiredSignatureError:
        return {"code": 401, "message": "Token 已过期!"}
    except jwt.InvalidTokenError:
        return {"code": 402, "message": "用户尚未登录!"}


"""
检查用户的锁定状态
"""
def check_account_lock_status(userid):
    lk = "account.lock.%s" % userid
    lock = db.rdb.get(lk)
    if lock and int(lock) > 9:
        return False
    return True



"""
用户锁定计数器
"""
def lock_account(userid):
    lk = "account.lock.%s" % userid
    db.rdb.incr(lk)
    db.rdb.expire(lk, 60 * 60 * 24)



"""
jwt token
"""
def authenticate(username, password):
    info = db.get_account_by_username(username)
    if not info:
        return jsonify({"code": 400, "message": "用户不存在"})
    else:
        if 'password' not in info or "userid" not in info:
            return jsonify({"code": 400, "message": "系统错误!"})
        if not check_account_lock_status(info["userid"]):
            return jsonify({"code": 400, "message": "账户已锁定!请在24小时后重试!"})
        if info["password"] == password:
            try:
                playload = {
                    "headers": {
                        "typ": "JWT",
                        "alg": "HS256",
                        "userid": info['userid']
                    },
                    "iss": 'ly',
                    "exp": datetime.datetime.utcnow() + datetime.timedelta(days=10, hours=0, minutes=15, seconds=0),
                    'iat': datetime.datetime.utcnow()
                }
                signature = jwt.encode(playload, SECRET_KEY, algorithm='HS256')
                return jsonify({"code": 200, "token": signature.decode(), "message": "登录成功!"})
            except Exception as e:
                print(e)
                return jsonify({"code": 400, "message": "create token error!"})
        else:
            lock_account(info["userid"])
            return jsonify({"code": 400, "message": "密码不正确"})



"""
请求频繁度检测
"""
def check_frequent(key):
    if db.rdb.get(key):
        return False
    db.rdb.set(key, key)
    db.rdb.expire(key, 10)
    return True



"""
注册
curl -X POST \
    http://localhost:8888/web/signup \
    -F username=b \
    -F password=2
"""
@app.route('/web/signup', methods=['POST'])
def web_signup():
    username = request.form.get('username')
    password = request.form.get('password')
    if not username or not password:
        return jsonify({"code": 400, "message": "参数错误! username! password!"})
    else:
        o = db.get_account_by_username( username )
        if o:
            return jsonify({"code": 400, "message": "用户已存在!"})
        else:
            db.create_account(username, password)
            return authenticate(username, password)



"""
登录
curl -X POST \
    http://localhost:8888/web/signin \
    -F username=b \
    -F password=2
"""
@app.route('/web/signin', methods=['POST'])
def web_signin():
    username = request.form.get('username')
    password = request.form.get('password')
    if not username or not password:
        return jsonify({"code": 400, "message": "参数错误! username! password!"})
    else:
        return authenticate(username, password)



"""
查账户余额
curl -X GET \
    http://localhost:8888/web/balances
    -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWFkZXJzIjp7InR5cCI6IkpXVCIsImFsZyI6IkhTMjU2IiwidXNlcmlkIjoiOTc3YjU3ODQxN2VhNGMyNDNmYzdiYmNiNjFkZTdiYjIifSwiaXNzIjoibHkiLCJleHAiOjE1NjU0Mjc5MTUsImlhdCI6MTU2NDU2MzAxNX0.7NLVW7IeO3lYgDdEt7w-hkNhyXy2tUeJ-WkT6L0k4_s"
"""
@app.route('/web/balances', methods=['GET'])
def web_balances():
    auth = identify(request)
    if auth['code'] == 200 and auth['data']:
        userid = auth["data"]["userid"]
        if not check_frequent(userid + ".web.balances"):
            return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
        balances = db.get_balances(userid)
        return jsonify({"code": 200, "data": balances})
    else:
        return jsonify({"code": 400, "message": auth["message"]})



"""
查成交
curl -X GET \
    http://localhost:8888/web/history?symbol=btc/cnyt \
    -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWFkZXJzIjp7InR5cCI6IkpXVCIsImFsZyI6IkhTMjU2IiwidXNlcmlkIjoiOTc3YjU3ODQxN2VhNGMyNDNmYzdiYmNiNjFkZTdiYjIifSwiaXNzIjoibHkiLCJleHAiOjE1NjU0Mjc5MTUsImlhdCI6MTU2NDU2MzAxNX0.7NLVW7IeO3lYgDdEt7w-hkNhyXy2tUeJ-WkT6L0k4_s"
"""
@app.route('/web/history', methods=['GET'])
def web_history():
    auth = identify(request)
    symbol = request.args.get('symbol')
    if not symbol:
        return jsonify({"code": 400, "message": "参数错误! symbol!"})
    if auth['code'] == 200 and auth['data']:
        userid = auth["data"]["userid"]
        if not check_frequent(userid + ".web.coin"):
            return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
        data = db.get_history(symbol, userid, limit=30)
        data = [o for o in data]
        return jsonify({"code": 200, "data": data})
    else:
        return jsonify({"code": 400, "message": auth["message"]})



"""
查委托 & 刷新委托状态
curl -X GET \
    http://localhost:8888/web/orders?symbol=swtc/cnyt \
    -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWFkZXJzIjp7InR5cCI6IkpXVCIsImFsZyI6IkhTMjU2IiwidXNlcmlkIjoiOTc3YjU3ODQxN2VhNGMyNDNmYzdiYmNiNjFkZTdiYjIifSwiaXNzIjoibHkiLCJleHAiOjE1NjU0Mjc5MTUsImlhdCI6MTU2NDU2MzAxNX0.7NLVW7IeO3lYgDdEt7w-hkNhyXy2tUeJ-WkT6L0k4_s"
"""
@app.route('/web/orders', methods=['GET'])
def web_orders():
    auth = identify(request)
    symbol = request.args.get('symbol')
    if not symbol:
        return jsonify({"code": 400, "message": "参数错误! symbol!"})
    if auth['code'] == 200 and auth['data']:
        userid = auth["data"]["userid"]
        if not check_frequent(userid + ".web.orders"):
            return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
        data = []
        for o in db.get_orders(symbol, userid=userid, process_cancel=True):
            data.append(o)
        return jsonify({"code": 200, "data": data})
    else:
        return jsonify({"code": 400, "message": auth["message"]})



"""
委托
curl -X POST \
    http://localhost:8888/web/order \
    -H 'Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWFkZXJzIjp7InR5cCI6IkpXVCIsImFsZyI6IkhTMjU2IiwidXNlcmlkIjoiOTc3YjU3ODQxN2VhNGMyNDNmYzdiYmNiNjFkZTdiYjIifSwiaXNzIjoibHkiLCJleHAiOjE1NjU0Mjc5MTUsImlhdCI6MTU2NDU2MzAxNX0.7NLVW7IeO3lYgDdEt7w-hkNhyXy2tUeJ-WkT6L0k4_s' \
    -F symbol=btc/cnyt \
    -F action=buy \
    -F price=1.0 \
    -F amount=268.0
"""
@app.route('/web/order', methods=['POST'])
def web_order():
    auth = identify(request)
    if auth['code'] != 200 or not auth['data']:
        return jsonify({"code": 400, "message": auth["message"]})
    symbol = request.form.get('symbol')
    action = request.form.get('action')
    price = request.form.get('price')
    amount = request.form.get('amount')
    '''
    验证参数
    '''
    if not symbol or not action or not price or not amount:
        return jsonify({"code": 400, "message": "参数错误! symbol! action! price! amount!"})
    symbol = symbol.lower()
    if '/' not in symbol:
        return jsonify({"code": 400, "message": "参数格式错误! symbol!"})
    action = action.lower()
    if action not in ["buy", "sell"]:
        return jsonify({"code": 400, "message": "参数错误! action not in [buy, sell]!"})
    sell_symbol, buy_symbol = symbol.split('/')
    try:
        amount = float(amount)
    except:
        return jsonify({"code": 400, "message": "参数格式错误! amount!"})
    '''
    验证用户状态
    '''
    userid = auth["data"]["userid"]
    if not check_frequent(userid + ".web.order"):
        return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
    user = db.get_account( userid )
    if not user:
        return jsonify({"code": 400, "message": "无效用户!"})
    '''
    验证余额状态
    '''
    if "balances" not in user:
        return jsonify({"code": 400, "message": "余额不足!"})
    if action == "buy":
        if buy_symbol not in user["balances"] or user["coins"][buy_symbol]["balances"] < amount:
            return jsonify({"code": 400, "message": "余额不足!"})
        user["coins"][buy_symbol]["balances"] -= amount
    if action == "sell":
        if sell_symbol not in user["balances"] or user["coins"][sell_symbol]["balances"] < amount:
            return jsonify({"code": 400, "message": "余额不足!"})
        user["coins"][sell_symbol]["balances"] -= amount
    '''
    更新用户余额
    '''
    db.update_account( userid, user )
    '''
    推送下单请求
    '''
    db.rdb.lpush("queue", "order,%s,%s,%s,%s,%s" % (userid, symbol, action, price, amount))
    return jsonify({"code": 200, "message": "委托请求已发送!"})





"""
撤单
curl -X POST \
    http://localhost:8888/web/cancel \
    -H 'Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWFkZXJzIjp7InR5cCI6IkpXVCIsImFsZyI6IkhTMjU2IiwidXNlcmlkIjoiOTc3YjU3ODQxN2VhNGMyNDNmYzdiYmNiNjFkZTdiYjIifSwiaXNzIjoibHkiLCJleHAiOjE1NjU0Mjc5MTUsImlhdCI6MTU2NDU2MzAxNX0.7NLVW7IeO3lYgDdEt7w-hkNhyXy2tUeJ-WkT6L0k4_s' \
    -F symbol=btc/cnyt \
    -F action=buy \
    -F seqno=9bb9a829eb9ffdf422926a636911262e
"""
@app.route('/web/cancel', methods=['POST'])
def web_cancel():
    auth = identify(request)
    symbol = request.form.get('symbol')
    action = request.form.get('action')
    seqno = request.form.get('seqno')
    if not seqno or not symbol or not action:
        return jsonify({"code": 400, "message": "参数错误! symbol! action! seqno!"})
    symbol = symbol.lower()
    if action not in ["buy", "sell"]:
        return jsonify({"code": 400, "message": "参数错误! action not in [buy, sell]!"})
    action = action.lower()
    if auth['code'] == 200 and auth['data']:
        userid = auth["data"]["userid"]
        if not check_frequent(userid + ".web.cancel"):
            return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
        db.rdb.lpush("queue", "cancel,%s,%s,%s,%s" % (auth["data"]["userid"], symbol, action, seqno))
        return jsonify({"code": 200, "message": "撤单请求已发送!"})
    else:
        return jsonify({"code": 400, "message": auth["message"]})



"""
提币申请
"""
@app.route("/web/withdraw", methods=["POST"])
def web_withdraw():
    '''
    验证认证状态
    '''
    auth = identify(request)
    if auth['code'] != 200 or not auth['data']:
        return jsonify({"code": 400, "message": auth["message"]})
    coin = request.form.get('coin')
    amount = request.form.get('amount')
    if not coin or not amount:
        return jsonify({"code": 400, "message": "参数错误! coin! amount!"})
    coin = coin.lower()
    try:
        amount = float(amount)
    except:
        return jsonify({"code": 400, "message": "参数错误! amount must be float!"})
    if amount <= 0.0:
        return jsonify({"code": 400, "message": "参数错误! amount must be > 0.0!"})
    '''
    验证用户状态
    '''
    userid = auth["data"]["userid"]
    if not check_frequent(userid + ".web.coin.withdraw"):
        return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
    user = db.get_account(userid)
    if not user:
        return jsonify({"code": 400, "message": "账户异常!"})
    '''
    验证余额状态
    '''
    if amount > user["coins"][coin]["balances"]:
        return jsonify({"code": 400, "message": "账户余额不足!"})
    o = db.get_address_by_withdraw( userid, coin )
    if not o:
        return jsonify({"code": 400, "message": "尚未添加提币地址!"})
    db.withdraw( userid, coin, amount, o["address"], o["memo"])
    return jsonify({"code": 200, "message": "提币请求已发送!请耐心等待!"})



"""
添加提币地址、重复执行将覆盖现有的数据
"""
@app.route("/web/address/withdraw/add", methods=["POST"])
def web_address_withdraw_add():
    auth = identify(request)
    coin = request.form.get('coin')
    address = request.form.get('address')
    memo = request.form.get('memo')
    if not coin or not address:
        return jsonify({"code": 400, "message": "参数错误! coin! address! memo!"})
    coin = coin.lower()
    if not memo:
        memo = ""
    if auth['code'] == 200 and auth['data']:
        userid = auth["data"]["userid"]
        if not check_frequent(userid + "/web/coin/address/withdraw/add"):
            return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
        db.set_address_by_withdraw( userid, coin, address, memo )
        return jsonify({"code": 200, "message": "成功添加提币地址!"})
    else:
        return jsonify({"code": 400, "message": auth["message"]})



"""
充币地址查询
"""
@app.route("/web/address/recharge/get", methods=["GET"])
def web_address_recharge_get():
    auth = identify(request)
    coin = request.form.get('coin')
    if not coin:
        return jsonify({"code": 400, "message": "参数错误! coin!"})
    coin = coin.lower()
    if auth['code'] == 200 and auth['data']:
        userid = auth["data"]["userid"]
        if not check_frequent(userid + "/web/coin/address/save/query"):
            return jsonify({"code": 400, "message": "请求太频繁! 请10秒钟后再试!"})
        o = db.get_address_by_recharge( userid, coin )
        if not o:
            return jsonify({"code": 400, "message": "不存在充币地址!"})
        return jsonify({"code": 200, "address": o["address"], "memo": o["memo"]})
    else:
        return jsonify({"code": 400, "message": auth["message"]})




if __name__ == '__main__':
    app.run(conf.flask.host, conf.flask.port)
